package converterOCL;

import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import facade.UserFacade;
import facade.kernel.AConstrainedElementConstraintOfConstrainedElementFacade;
import parser.util.ReflectiveVisitor;
import parser.util.Utility;
import project.Project;
import uml2.associationclasses.AssociationClass;
import uml2.kernel.AConstrainedElementConstraintOfConstrainedElement;
import uml2.kernel.Constraint;
import uml2.kernel.DataType;
import uml2.kernel.Element;
import uml2.kernel.Operation;
import uml2.kernel.Parameter;
import uml2.kernel.ParameterDirectionKindEnum;
import uml2.kernel.Property;
import uml2.kernel.UmlClass;
import uml2.kernel.ValueSpecification;
import uml2.ocl2.AssociationClassCallExp;
import uml2.ocl2.BagType;
import uml2.ocl2.BooleanLiteralExp;
import uml2.ocl2.CollectionItem;
import uml2.ocl2.CollectionLiteralExp;
import uml2.ocl2.CollectionLiteralPart;
import uml2.ocl2.CollectionRange;
import uml2.ocl2.CollectionType;
import uml2.ocl2.EnumLiteralExp;
import uml2.ocl2.ExpressionInOcl;
import uml2.ocl2.IfExp;
import uml2.ocl2.IntegerLiteralExp;
import uml2.ocl2.IterateExp;
import uml2.ocl2.IteratorExp;
import uml2.ocl2.LetExp;
import uml2.ocl2.OclExpression;
import uml2.ocl2.OperationCallExp;
import uml2.ocl2.OrderedSetType;
import uml2.ocl2.PropertyCallExp;
import uml2.ocl2.RealLiteralExp;
import uml2.ocl2.SequenceType;
import uml2.ocl2.SetType;
import uml2.ocl2.StringLiteralExp;
import uml2.ocl2.TupleLiteralExp;
import uml2.ocl2.TupleLiteralPart;
import uml2.ocl2.TupleType;
import uml2.ocl2.TypeExp;
import uml2.ocl2.Variable;
import uml2.ocl2.VariableExp;

public class HumanOutputGenerator extends ReflectiveVisitor{

	Utility ut;
	UserFacade uf;
	Project p;
	String tab = "";
	public HumanOutputGenerator(Project p) {
		super("generate");
		this.p = p;
		this.uf = new UserFacade(p);
		this.ut = new Utility(uf,p);
	}
	
	public LinkedList getHumanCode() {
		LinkedList stringresults = new LinkedList();
		
		// First, we have to obtain the all constraints in the repository
		UserFacade uf = new UserFacade(p);
		LinkedList constraints = (LinkedList) uf.getAllConstraints();
		
		Iterator it = constraints.iterator();
		while (it.hasNext()) {
			Constraint c = (Constraint) it.next();
			stringresults.add(generation(c));
		}
		return stringresults;
	}
	
	// First step of the visitor
	public String generation(Element e) {
		String result = "";
		try {
			result = (String) visit(e);
		} catch (NoSuchMethodException ex) {
			//some  method is missing in the visitor implementation
			ex.printStackTrace();
		} catch (java.lang.reflect.InvocationTargetException ex){
			Throwable nestedException = ex.getTargetException();
			nestedException.printStackTrace();
		}
		return result;
	}
	
	
	public String generate(Constraint c) {
		
		String name = c.getName();
		ValueSpecification spec = c.getSpecification();
		
		AConstrainedElementConstraintOfConstrainedElementFacade acecocef = new AConstrainedElementConstraintOfConstrainedElementFacade(p);
		AConstrainedElementConstraintOfConstrainedElement acecoce = acecocef.getAConstrainedElementConstraintOfConstrainedElement();
		List constrainedElement = acecoce.getConstrainedElement(c);
		Element ce = (Element) constrainedElement.get(0);
		
		tab = "    ";
		String conel = generation(ce);
		tab = "  ";
		String sp = generation(spec);
		tab = "  ";
		
		return "Constraint "+name+
		       "\n"+tab+"ConstrainedElement"+conel+
		       "\n"+tab+"Specification"+sp;
	}
	
	public String generate(ExpressionInOcl eio) {
		tab += "  ";
		String aux = tab;
		tab += "    ";
		String body = generation(eio.getBodyExpression());
		tab = aux+"    ";
		String ctxvar = generation(eio.getContextVariable());
		tab = aux;
		String symbol = eio.getSymbol();
		
		return "\n"+aux+"ExpressionInOcl "+symbol+
		       "\n"+aux+"  ContextVariable"+ctxvar+
		       "\n"+aux+"  BodyExpression"+body;
	}
	
	public String generate(OperationCallExp oce) {
		String aux = tab;
		tab += "  ";
		
		String params = "\n"+tab+"Argument";
		tab += "  ";
		Collection c = oce.getArgument();
		for (Iterator iterator = c.iterator(); iterator.hasNext();) {
			OclExpression par = (OclExpression) iterator.next();
			params+=tab+generation(par);
			tab = aux +"    ";
		}
		tab = aux +"    ";
		String source = "\n"+aux +"  Source"+ generation(oce.getSource());
		
		tab = aux+"    ";
		String oper = "\n"+aux +"  ReferredOperation"+generation(oce.getReferredOperation());
		
		tab = aux+"    ";
		String type = "\n"+aux+"  Type"+generation(oce.getType());
		
		tab = aux;
		if (params.equals("\n"+tab+"  Argument")) return "\n"+tab+"OperationCallExp "+oper+source+type;
		return "\n"+tab+"OperationCallExp "+oper+params+source+type;
	}
	
	public String generate(UmlClass uc) {
		return "\n"+tab+"Class "+uc.getName();
	}
	
	public String generate(TypeExp te) {
		
		String type = generation(te.getType());
		String reftype = generation(te.getReferredType());
		
		return te.getReferredType().getName();
	}
	
	public String generate(IntegerLiteralExp ile) {
		return "\n"+tab+"IntegerLiteralExp "+ile.getIntegerSymbol();
	}
	
	public String generate(RealLiteralExp rle) {
		return "\n"+tab+"RealLiteralExp "+rle.getRealSymbol();
	}
	
	public String generate(BooleanLiteralExp ble) {
		return "\n"+tab+"BooleanLiteralExp ";
	}
	
	public String generate(StringLiteralExp sle) {
		return "\n"+tab+"StringLiteralExp "+sle.getStringSymbol();
	}
	
	public String generate(EnumLiteralExp ele) {
		String aux = tab;
		
		tab = aux+"    ";
		String ref = "\n"+aux +"  ReferredEnumLiteral"+ generation(ele.getReferredEnumLiteral());
		
		tab = aux+"    ";
		String type = "\n"+aux+"  Type"+generation(ele.getType());
		
		tab = aux;
		
		return "\n"+tab+"EnumLiteralExp "+ref+type;
	}
	
	public String generate(Operation op) {
		return "\n"+tab+"Operation "+op.getName();
	}

	public String generate(PropertyCallExp pce) {
		String aux = tab;
		
		tab = aux+"    ";
		String source = "\n"+aux +"  Source"+ generation(pce.getSource());
		
		tab = aux+"    ";
		String prop = "\n"+aux +"  ReferredProperty"+generation(pce.getReferredProperty());
		
		tab = aux+"    ";
		String type = "\n"+aux+"  Type"+generation(pce.getType());
		
		tab = aux;
	
		return "\n"+tab+"PropertyCallExp "+prop+source+type;
	}
	
	public String generate(AssociationClassCallExp acce) {
		String aux = tab;
		
		tab = aux+"    ";
		String source = "\n"+aux +"  Source"+ generation(acce.getSource());
		
		tab = aux+"    ";
		String ac = "\n"+aux +"  ReferredAssociationClass"+generation(acce.getReferredAssociationClass());
		
		tab = aux+"    ";
		String type = "\n"+aux+"  Type"+generation(acce.getType());
		
		tab = aux;
	
		return "\n"+tab+"AssociationClassCallExp "+ac+source+type;
	}
	
	public String generate(IfExp ie) {
		String aux = tab;
		
		tab = aux+"    ";
		String cond = "\n"+aux +"  Condition"+ generation(ie.getCondition());
		
		tab = aux+"    ";
		String then = "\n"+aux +"  ThenExpression"+generation(ie.getThenExpression());
		
		tab = aux+"    ";
		String elseexp = "\n"+aux +"  ElseExpression"+generation(ie.getElseExpression());
		
		tab = aux+"    ";
		String type = "\n"+aux+"  Type"+generation(ie.getType());
		
		tab = aux;
	
		return "\n"+tab+"IfExp "+cond+then+elseexp+type;
	}
	
	public String generate(IteratorExp ie) {
		String aux = tab;
		tab += "  ";
		
		String iters = "\n"+tab+"Iterator";
		tab += "  ";
		Collection c = ie.getIterator();
		for (Iterator iterator = c.iterator(); iterator.hasNext();) {
			Variable var = (Variable) iterator.next();
			iters+=tab+generation(var);
			tab = aux +"    ";
		}
		tab = aux +"    ";
		String source = "\n"+aux +"  Source"+ generation(ie.getSource());
		
		tab = aux+"    ";
		String body = "\n"+aux +"  Body"+generation(ie.getBody());
		
		tab = aux+"    ";
		String type = "\n"+aux+"  Type"+generation(ie.getType());
		
		tab = aux;
		if (iters.equals("\n"+tab+"  Iterator")) return "\n"+tab+"IteratorExp "+ie.getName()+source+body+type;
		return "\n"+tab+"IteratorExp "+ie.getName()+iters+source+body+type;
	}
	
	public String generate(Property p) {
		String aux = tab;
		tab += "    ";
		String type = "\n"+aux+"  Type"+generation(p.getType());
		tab = aux;
		return "\n"+tab+"Property "+p.getName()+type;
	}
	
	public String generate(IterateExp ie) {
		String aux = tab;
		tab += "  ";
		
		tab = aux;
		return "\n"+tab+"IterateExp "+ie.getName();
	}
	
	public String generate(LetExp le) {
		String aux = tab;
		
		tab = aux+"    ";
		String var = "\n"+aux +"  Variable"+ generation(le.getVariable());
		
		tab = aux+"    ";
		String in = "\n"+aux +"  In"+generation(le.getIn());
		
		tab = aux+"    ";
		String type = "\n"+aux+"  Type"+generation(le.getType());
		
		tab = aux;
	
		return "\n"+tab+"IfExp "+var+in+type;
	}

	public String generate(Variable v) {
		String aux = tab;
		String init = "";
		if (v.getInitExpression()!=null) {
			tab += "    ";
			init = "\n"+aux+"  InitExpression"+generation(v.getInitExpression());
		}
		tab += "    ";
		String type = "\n"+aux+"  Type"+generation(v.getType());
		tab = aux;
		return "\n"+aux+"Variable "+v.getName()+init+type;
	}
	
	public String generate(VariableExp ve) {
		String aux = tab;
		tab += "    ";
		String type = generation(ve.getType());
		tab = aux+"    ";
		String var = generation(ve.getReferredVariable());
		tab = aux;
		
		type = "\n"+tab+"  "+"Type"+type;
		var = "\n"+tab+"  "+"ReferredVariable"+var;
		
		return "\n"+aux+"VariableExp "+var+type;
	}
	
	public String generate(CollectionLiteralExp cle) {
		String aux = tab;
		String kind = cle.getKind().toString();
		tab += "  ";
		
		String parts = "\n"+tab+"Part";
		tab += "  ";
		Collection c = cle.getPart();
		for (Iterator iterator = c.iterator(); iterator.hasNext();) {
			CollectionLiteralPart part = (CollectionLiteralPart) iterator.next();
			parts+=tab+generation(part);
			tab = aux +"    ";
		}
		
		tab = aux+"    ";
		String type = "\n"+aux+"  Type"+generation(cle.getType());
		
		tab = aux;
		
		if (parts.equals("\n"+tab+"  Part")) {
			return "\n"+tab+"CollectionLiteralExp "+kind+type;
		}
		return "\n"+tab+"CollectionLiteralExp "+kind+parts+type;
	}
 
	public String generate(CollectionRange cr) {
		String aux = tab;
		
		tab = aux+"    ";
		String first = "\n"+aux +"  First"+ generation(cr.getFirst());
		
		tab = aux+"    ";
		String last = "\n"+aux +"  Last"+generation(cr.getLast());
		
		tab = aux+"    ";
		String type = "\n"+aux+"  Type"+generation(cr.getType());
		
		tab = aux;
	
		return "\n"+tab+"CollectionRange "+first+last+type;
	}
	
	public String generate(CollectionItem ci) {
		String aux = tab;
		
		tab = aux+"    ";
		String item = "\n"+aux +"  Item"+ generation(ci.getItem());
		
		tab = aux+"    ";
		String type = "\n"+aux+"  Type"+generation(ci.getType());
		
		tab = aux;
	
		return "\n"+tab+"CollectionRange "+item+type;
	}
	
	public String generate(TupleLiteralExp tle) {
		String aux = tab;
		tab += "  ";
		
		String parts = "\n"+tab+"Part";
		tab += "  ";
		Collection c = tle.getPart();
		for (Iterator iterator = c.iterator(); iterator.hasNext();) {
			TupleLiteralPart part = (TupleLiteralPart) iterator.next();
			parts+=tab+generation(part);
			tab = aux +"    ";
		}
		
		tab = aux+"    ";
		String type = "\n"+aux+"  Type"+generation(tle.getType());
		
		tab = aux;
	
		return "\n"+tab+"TupleLiteralExp "+parts+type;
	}
	
	public String generate(TupleLiteralPart tlp) {
		String aux = tab;
		
		tab = aux+"    ";
		String attr = "\n"+aux +"  Attribute"+ generation(tlp.getAttribute());
		
		tab = aux+"    ";
		String value = "\n"+aux +"  Value"+generation(tlp.getValue());
		
		tab = aux+"    ";
		String type = "\n"+aux+"  Type"+generation(tlp.getType());
		
		tab = aux;
	
		return "\n"+tab+"TupleLiteralPart "+attr+value+type;
	}
	
	public String generate(TupleType tt) {
		String aux = tab;
		
		String parts = "\n"+tab+"  OwnedAttribute";
		tab += "    ";
		Collection c = tt.getOwnedAttribute();
		for (Iterator iterator = c.iterator(); iterator.hasNext();) {
			Property p = (Property) iterator.next();
			parts+=tab+generation(p);
			tab = aux +"    ";
		}
		
		tab = aux;
	
		return "\n"+tab+"TupleType "+parts;
	}
	
	public String generate(CollectionType ct) {
		String aux = tab;
		tab += "    ";
		String elem = "";
		if (ct.getElementType()!=null) {
			elem = "\n"+aux+"  ElementType"+generation(ct.getElementType());
		}
		tab = aux;
		
		String result = "\n"+tab;
		if (ct instanceof SetType) {
			result += "SetType";
		}
		else if (ct instanceof OrderedSetType) {
			result += "OrderedSetType";
		}
		else if (ct instanceof BagType) {
			result += "BagType";
		}
		else if (ct instanceof SequenceType) {
			result += "SequenceType";
		}
		return result+elem;
	}
	
	public String generate(DataType dt) {
		return "\n"+tab+"DataType "+dt.getName();
	}
	
	public static void main(String args[]) throws Exception {
		Project p = new Project();
		p.importXMI("/Users/lotendil/Desktop/c4yc5.xmi");
		HumanOutputGenerator hog = new HumanOutputGenerator(p);
		List code = hog.getHumanCode();
		for (Iterator iterator = code.iterator(); iterator.hasNext();) {
			String s = (String) iterator.next();
			System.out.println(s);
			System.out.println();
		}
		p.closeProject();
	}
}



